/* Wang Wen(Albert Wang) jawangwen@gmail.com */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "rt_refer.h"

#define SYM_TBL_CREATE_CMD_SIZE     1024
#define SYM_TMP_CREATE_CMD_SIZE     1024
#define FILE_NAME_MAX               256

#define SYM_TBL_FILE_EXTENSION      ".sym_tbl"
#define SYM_TMP_FILE_EXTENSION      ".sym_tmp"

static int    g_flag;
static char   g_exec_name[FILE_NAME_MAX];
static char   g_sym_tbl_file[FILE_NAME_MAX];
static char   g_sym_tmp_file[FILE_NAME_MAX];

int refer_init (const char * exec_name)
{
    const char * cmd_fmt = "nm -C -l %s"
                           " | awk '{if ( ($1~/^[0-9A-Fa-f]+$/) && ($2~/^[tT]$/)"
                           " && !($3~/^\\.text$/) ) {print $0}}'"
                           " > %s";
    const unsigned int   wildcard_len  = 2 * sizeof("%s");
    char                 cmd[SYM_TBL_CREATE_CMD_SIZE];
    unsigned int         cmd_fmt_len;
    unsigned int         cmd_len;

    if (exec_name == NULL) {
        RT_REFER_ERROR("exec_name<NULL> invalid!");
        return -1;
    }
    if (g_flag) {
        RT_REFER_WARN("refer_init already!");
        return 0;
    }

    if (strlen(g_exec_name) < FILE_NAME_MAX - 1) {
        strcpy(g_exec_name, exec_name);
    } else {
        RT_REFER_ERROR("exec_name_len<%u> invaild, must be less than %u!",
                      strlen(g_exec_name),
                      FILE_NAME_MAX - 1);
        return -1;
    }
    if (strlen(g_exec_name) + strlen(SYM_TBL_FILE_EXTENSION) < FILE_NAME_MAX - 1) {
        strcpy(g_sym_tbl_file, exec_name);
        strcat(g_sym_tbl_file, SYM_TBL_FILE_EXTENSION);
    } else {
        RT_REFER_ERROR("sym_tbl_file_len<%u> invaild, must be less than %u!",
                       strlen(g_exec_name) + strlen(SYM_TBL_FILE_EXTENSION),
                       FILE_NAME_MAX - 1);
        return -1;
    }
    if (strlen(g_exec_name) + strlen(SYM_TMP_FILE_EXTENSION) < FILE_NAME_MAX - 1) {
        strcpy(g_sym_tmp_file, exec_name);
        strcat(g_sym_tmp_file, SYM_TMP_FILE_EXTENSION);
    } else {
        RT_REFER_ERROR("sym_tmp_file_len<%u> invaild, must be less than %u!",
                       strlen(g_exec_name) + strlen(SYM_TMP_FILE_EXTENSION),
                       FILE_NAME_MAX - 1);
        return -1;
    }

    cmd_fmt_len = strlen(cmd_fmt) - wildcard_len;
    cmd_len     = cmd_fmt_len + strlen(g_exec_name) + strlen(g_sym_tbl_file);
    if (cmd_len >= SYM_TBL_CREATE_CMD_SIZE - 1) {
        RT_REFER_ERROR("cmd_len<%u> invaild, must be less than %u",
                       cmd_len,
                       (unsigned int)(SYM_TBL_CREATE_CMD_SIZE - 1));
        return -1;
    }

    sprintf(cmd, "rm -f %s", g_sym_tbl_file);
    RT_REFER_TRACE("[system] %s", cmd);
    system(cmd);

    sprintf(cmd, cmd_fmt, g_exec_name, g_sym_tbl_file);
    RT_REFER_TRACE("[system] %s", cmd);
    system(cmd);

    g_flag = 1;

    return 0;
}

int refer_func (void ** refer_ptr, const char * func_name, const char * file_name)
{
    const char * cmd_fmt = "awk '{if ( ($1~/^[0-9A-Fa-f]+$/)"
                           " && ($2~/^[tT]$/)"
                           " && ($3 == \"%s\")"
                           " && ($4~/'%s'/)"
                           " ) {print $1}}' %s"
                           " > %s";
    const unsigned int   wildcard_len  = 4 * sizeof("%s");
    FILE               * file_handle;
    char                 cmd[SYM_TMP_CREATE_CMD_SIZE];
    unsigned int         cmd_fmt_len;
    unsigned int         cmd_len;

    if (refer_ptr == NULL) {
        RT_REFER_ERROR("refer_ptr<NULL> invalid!");
        return -1;
    }
    if (func_name == NULL) {
        RT_REFER_ERROR("func_name<NULL> invalid!");
        return -1;
    }
    if (file_name == NULL) {
        RT_REFER_ERROR("file_name<NULL> invalid!");
        return -1;
    }
    if (!g_flag) {
        RT_REFER_ERROR("RTReferInit not done!");
        return -1;
    }

    cmd_fmt_len = strlen(cmd_fmt) - wildcard_len;
    cmd_len     = cmd_fmt_len + strlen(func_name) + strlen(file_name) + strlen(g_sym_tmp_file);
    if (cmd_len >= SYM_TMP_CREATE_CMD_SIZE - 1) {
        RT_REFER_ERROR("cmd_len<%u> invaild, must be less than %d",
                       cmd_len,
                       (unsigned int)(SYM_TMP_CREATE_CMD_SIZE - 1));
        return -1;
    }

    sprintf(cmd, "rm -f %s", g_sym_tmp_file);
    RT_REFER_TRACE("[system] %s", cmd);
    system(cmd);

    sprintf(cmd, cmd_fmt, func_name, file_name, g_sym_tbl_file, g_sym_tmp_file);
    RT_REFER_TRACE("[system] %s", cmd);
    system(cmd);

    file_handle = fopen(g_sym_tmp_file, "rb");
    if (!file_handle) {
        RT_REFER_ERROR("cannot open sym_tmp_file<%s>", g_sym_tmp_file);
        return -1;
    }
    if (fscanf(file_handle, "%x", (unsigned int *)refer_ptr) == EOF 
        || *refer_ptr == NULL) {
        RT_REFER_ERROR("sym_tmp_file<%s> empty!", g_sym_tmp_file);
        fclose(file_handle);
        return -1;
    }
    fclose(file_handle);

    return 0;
}

